home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Shells / tcsh / Source / tw.comp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  13.3 KB  |  595 lines

  1. /* $Header: /u/christos/src/tcsh-6.03/RCS/tw.comp.c,v 1.20 1992/10/10 18:17:34 christos Exp $ */
  2. /*
  3.  * tw.comp.c: File completion builtin
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #include "sh.h"
  38.  
  39. RCSID("$Id: tw.comp.c,v 1.20 1992/10/10 18:17:34 christos Exp $")
  40.  
  41. #include "tw.h"
  42. #include "ed.h"
  43. #include "tc.h"
  44.  
  45. /* #define TDEBUG */
  46. struct varent completions;
  47.  
  48. static int            tw_result    __P((Char *, Char **));
  49. static Char        **tw_find    __P((Char *, struct varent *, int));
  50. static Char          *tw_tok    __P((Char *));
  51. static bool           tw_pos    __P((Char *, int));
  52. static void            tw_pr        __P((Char **));
  53. static int            tw_match    __P((Char *, Char *));
  54. static void           tw_prlist    __P((struct varent *));
  55. static Char           *tw_dollar    __P((Char *,Char **, int, Char *, 
  56.                          int, char *));
  57.  
  58. /* docomplete():
  59.  *    Add or list completions in the completion list
  60.  */
  61. /*ARGSUSED*/
  62. void
  63. docomplete(v, t)
  64.     Char **v;
  65.     struct command *t;
  66. {
  67.     register struct varent *vp;
  68.     register Char *p;
  69.  
  70.     v++;
  71.     p = *v++;
  72.     if (p == 0)
  73.     tw_prlist(&completions);
  74.     else if (*v == 0) {
  75.     vp = adrof1(strip(p), &completions);
  76.     if (vp)
  77.         tw_pr(vp->vec), xputchar('\n');
  78.     }
  79.     else
  80.     set1(strip(p), saveblk(v), &completions);
  81. } /* end docomplete */
  82.  
  83.  
  84. /* douncomplete():
  85.  *    Remove completions from the completion list
  86.  */
  87. /*ARGSUSED*/
  88. void
  89. douncomplete(v, t)
  90.     Char **v;
  91.     struct command *t;
  92. {
  93.     unset1(v, &completions);
  94. } /* end douncomplete */
  95.  
  96.  
  97. /* tw_prlist():
  98.  *    Pretty print a list of variables
  99.  */
  100. static void
  101. tw_prlist(p)
  102.     struct varent *p;
  103. {
  104.     register struct varent *c;
  105.  
  106.     if (setintr)
  107. #ifdef BSDSIGS
  108.     (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
  109. #else                /* BSDSIGS */
  110.     (void) sigrelse(SIGINT);
  111. #endif                /* BSDSIGS */
  112.  
  113.     for (;;) {
  114.     while (p->v_left)
  115.         p = p->v_left;
  116. x:
  117.     if (p->v_parent == 0)    /* is it the header? */
  118.         return;
  119.     xprintf("%s\t", short2str(p->v_name));
  120.     tw_pr(p->vec);
  121.     xputchar('\n');
  122.     if (p->v_right) {
  123.         p = p->v_right;
  124.         continue;
  125.     }
  126.     do {
  127.         c = p;
  128.         p = p->v_parent;
  129.     } while (p->v_right == c);
  130.     goto x;
  131.     }
  132. } /* end tw_prlist */
  133.  
  134.  
  135. /* tw_pr():
  136.  *    Pretty print a completion, adding single quotes around 
  137.  *    a completion argument and collapsing multiple spaces to one.
  138.  */
  139. static void
  140. tw_pr(cmp)
  141.     Char **cmp;
  142. {
  143.     bool sp, osp;
  144.     Char *ptr;
  145.  
  146.     for (; *cmp; cmp++) {
  147.     xputchar('\'');
  148.     for (osp = 0, ptr = *cmp; *ptr; ptr++) {
  149.         sp = Isspace(*ptr);
  150.         if (sp && osp)
  151.         continue;
  152.         xputchar(*ptr);
  153.         osp = sp;
  154.     }
  155.     xputchar('\'');
  156.     if (cmp[1])
  157.         xputchar(' ');
  158.     }
  159. } /* end tw_pr */
  160.  
  161.  
  162. /* tw_find():
  163.  *    Find the first matching completion. 
  164.  *    For commands we only look at names that start with -
  165.  */
  166. static Char **
  167. tw_find(nam, vp, cmd)
  168.     Char   *nam;
  169.     register struct varent *vp;
  170.     int cmd;
  171. {
  172.     register Char **rv;
  173.  
  174.     for (vp = vp->v_left; vp; vp = vp->v_right) {
  175.     if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL)
  176.         return rv;
  177.     if (cmd) {
  178.         if (vp->v_name[0] != '-')
  179.         continue;
  180.         if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL)
  181.         return vp->vec;
  182.     }
  183.     else
  184.         if (Gmatch(nam, vp->v_name) && vp->vec != NULL)
  185.         return vp->vec;
  186.     }
  187.     return NULL;
  188. } /* end tw_find */
  189.  
  190.  
  191. /* tw_pos():
  192.  *    Return true if the position is within the specified range
  193.  */
  194. static bool
  195. tw_pos(ran, wno)
  196.     Char *ran;
  197.     int      wno;
  198. {
  199.     Char *p;
  200.  
  201.     if (ran[0] == '*' && ran[1] == '\0')
  202.     return 1;
  203.  
  204.     for (p = ran; *p && *p != '-'; p++)
  205.     continue;
  206.  
  207.     if (*p == '\0')            /* range == <number> */
  208.     return wno == getn(ran);
  209.     
  210.     if (ran == p)            /* range = - <number> */
  211.     return wno <= getn(&ran[1]);
  212.     *p++ = '\0';
  213.  
  214.     if (*p == '\0')            /* range = <number> - */
  215.     return getn(ran) <= wno;
  216.     else                /* range = <number> - <number> */
  217.     return (getn(ran) <= wno) && (wno <= getn(p));
  218.        
  219. } /* end tw_pos */
  220.  
  221.  
  222. /* tw_tok():
  223.  *    Return the next word from string, unquoteing it.
  224.  */
  225. static Char *
  226. tw_tok(str)
  227.     Char *str;
  228. {
  229.     static Char *bf = NULL;
  230.  
  231.     if (str != NULL)
  232.     bf = str;
  233.     
  234.     /* skip leading spaces */
  235.     for (; *bf && Isspace(*bf); bf++)
  236.     continue;
  237.  
  238.     for (str = bf; *bf && !Isspace(*bf); bf++) {
  239.     if (ismeta(*bf))
  240.         return (Char *) -1;
  241.     *bf = *bf & ~QUOTE;
  242.     }
  243.     if (*bf != '\0')
  244.     *bf++ = '\0';
  245.  
  246.     return *str ? str : NULL;
  247. } /* end tw_tok */
  248.  
  249.  
  250. /* tw_match():
  251.  *    Match a string against the pattern given.
  252.  *    and return the number of matched characters
  253.  *    in a prefix of the string.
  254.  */
  255. static int
  256. tw_match(str, pat)
  257.     Char *str, *pat;
  258. {
  259.     Char *estr;
  260.     int rv = Gnmatch(str, pat, &estr);
  261. #ifdef TDEBUG
  262.     xprintf("Gnmatch(%s, ", short2str(str));
  263.     xprintf("%s, ", short2str(pat));
  264.     xprintf("%s) = %d [%d]\n", short2str(estr), rv, estr - str);
  265. #endif /* TDEBUG */
  266.     return (rv ? estr - str : 0);
  267. }
  268.  
  269.  
  270. /* tw_result():
  271.  *    Return what the completion action should be depending on the
  272.  *    string
  273.  */
  274. static int
  275. tw_result(act, pat)
  276.     Char *act, **pat;
  277. {
  278.     int looking;
  279.     static Char* res = NULL;
  280.  
  281.     if (res != NULL)
  282.     xfree((ptr_t) res), res = NULL;
  283.  
  284.     switch (act[0] & ~QUOTE) {
  285.     case 'C':
  286.     looking = TW_COMPLETION;
  287.     break;
  288.     case 'S':
  289.     looking = TW_SIGNAL;
  290.     break;
  291.     case 'a':
  292.     looking = TW_ALIAS;
  293.     break;
  294.     case 'b':
  295.     looking = TW_BINDING;
  296.     break;
  297.     case 'c':
  298.     looking = TW_COMMAND;
  299.     break;
  300.     case 'd':
  301.     looking = TW_DIRECTORY;
  302.     break;
  303.     case 'e':
  304.     looking = TW_ENVVAR;
  305.     break;
  306.     case 'f':
  307.     looking = TW_FILE;
  308.     break;
  309.     case 'j':
  310.     looking = TW_JOB;
  311.     break;
  312.     case 'l':
  313.     looking = TW_LIMIT;
  314.     break;
  315.     case 'n':
  316.     looking = TW_NONE;
  317.     break;
  318.     case 'p':
  319.     looking = TW_PATHNAME;
  320.     break;
  321.     case 's':
  322.     looking = TW_SHELLVAR;
  323.     break;
  324.     case 't':
  325.     looking = TW_TEXT;
  326.     break;
  327.     case 'v':
  328.     looking = TW_VARIABLE;
  329.     break;
  330.     case 'u':
  331.     looking = TW_USER;
  332.     break;
  333.     case 'x':
  334.     looking = TW_EXPLAIN;
  335.     break;
  336.  
  337.     case '$':
  338.     *pat = res = Strsave(&act[1]);
  339.     (void) strip(res);
  340.     return(TW_VARLIST);
  341.  
  342.     case '(':
  343.     *pat = res = Strsave(&act[1]);
  344.     if ((act = Strchr(res, ')')) != NULL)
  345.         *act = '\0';
  346.     (void) strip(res);
  347.     return TW_WORDLIST;
  348.  
  349.     case '`':
  350.     res = Strsave(act);
  351.     if ((act = Strchr(&res[1], '`')) != NULL)
  352.         *++act = '\0';
  353.     
  354.     if (didfds == 0) {
  355.         /*
  356.          * Make sure that we have some file descriptors to
  357.          * play with, so that the processes have at least 0, 1, 2
  358.          * open
  359.          */
  360.         (void) dcopy(SHIN, 0);
  361.         (void) dcopy(SHOUT, 1);
  362.         (void) dcopy(SHDIAG, 2);
  363.     }
  364.     if ((act = globone(res, G_APPEND)) != NULL) {
  365.         xfree((ptr_t) res), res = NULL;
  366.         *pat = res = Strsave(act);
  367.         xfree((ptr_t) act);
  368.         return TW_WORDLIST;
  369.     }
  370.     return TW_ZERO;
  371.  
  372.     default:
  373.     stderror(ERR_COMPCOM, short2str(act));
  374.     return TW_ZERO;
  375.     }
  376.  
  377.     switch (act[1] & ~QUOTE) {
  378.     case '\0':
  379.     return looking;
  380.  
  381.     case ':':
  382.     *pat = res = Strsave(&act[2]);
  383.     (void) strip(res);
  384.     return looking;
  385.  
  386.     default:
  387.     stderror(ERR_COMPCOM, short2str(act));
  388.     return TW_ZERO;
  389.     }
  390. } /* end tw_result */
  391.         
  392.  
  393. /* tw_dollar():
  394.  *    Expand $<n> args in buffer
  395.  */
  396. static Char *
  397. tw_dollar(str, wl, nwl, buffer, sep, msg)
  398.     Char *str, **wl;
  399.     int nwl;
  400.     Char *buffer;
  401.     int sep;
  402.     char *msg;
  403. {
  404.     Char *sp, *bp = buffer, *ebp = &buffer[MAXPATHLEN];
  405.  
  406.     for (sp = str; *sp && *sp != sep && bp < ebp;)
  407.     if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[2])) {
  408.         int num;
  409.         sp += 2;
  410.         for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0')
  411.         continue;
  412.         if (num < nwl) {
  413.         Char *ptr;
  414.         for (ptr = wl[num]; *ptr && bp < ebp - 1; *bp++ = *ptr++)
  415.             continue;
  416.         
  417.         }
  418.     }
  419.     else
  420.         *bp++ = *sp++;
  421.  
  422.     *bp = '\0';
  423.  
  424.     if (*sp++ == sep)
  425.     return sp;
  426.  
  427.     stderror(ERR_COMPILL, msg, short2str(str));
  428.     return --sp;
  429. } /* end tw_dollar */
  430.         
  431.  
  432. /* tw_complete():
  433.  *    Return the appropriate completion for the command
  434.  *
  435.  *    valid completion strings are:
  436.  *    p/<range>/<completion>/[<suffix>/]    positional
  437.  *    c/<pattern>/<completion>/[<suffix>/]    current word ignore pattern
  438.  *    C/<pattern>/<completion>/[<suffix>/]    current word with pattern
  439.  *    n/<pattern>/<completion>/[<suffix>/]    next word
  440.  *    N/<pattern>/<completion>/[<suffix>/]    next-next word
  441.  */
  442. int
  443. tw_complete(line, word, pat, looking, suf)
  444.     Char *line, **word, **pat;
  445.     int looking, *suf;
  446. {
  447.     Char buf[MAXPATHLEN + 1], **vec, *ptr; 
  448.     Char *wl[MAXPATHLEN/6];
  449.     static Char nomatch[2] = { (Char) -1, 0x00 };
  450.     int wordno, n;
  451.  
  452.     copyn(buf, line, MAXPATHLEN);
  453.  
  454.     /* find the command */
  455.     if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == (Char*) -1)
  456.     return TW_ZERO;
  457.  
  458.     /*
  459.      * look for hardwired command completions using a globbing
  460.      * search and for arguments using a normal search.
  461.      */
  462.     if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) == NULL)
  463.     return looking;
  464.  
  465.     /* tokenize the line one more time :-( */
  466.     for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL &&
  467.               wl[wordno] != (Char *) -1; wordno++)
  468.     continue;
  469.  
  470.     if (wl[wordno] == (Char *) -1)    /* Found a meta character */
  471.     return TW_ZERO;            /* de-activate completions */
  472. #ifdef TDEBUG
  473.     {
  474.     int i;
  475.     for (i = 0; i < wordno; i++)
  476.         xprintf("'%s' ", short2str(wl[i]));
  477.     xprintf("\n");
  478.     }
  479. #endif /* TDEBUG */
  480.  
  481.     /* if the current word is empty move the last word to the next */
  482.     if (**word == '\0') {
  483.     wl[wordno] = *word;
  484.     wordno++;
  485.     }
  486.     wl[wordno] = NULL;
  487.     
  488.  
  489. #ifdef TDEBUG
  490.     xprintf("\r\n");
  491.     xprintf("  w#: %d\n", wordno);
  492.     xprintf("line: %s\n", short2str(line));
  493.     xprintf(" cmd: %s\n", short2str(wl[0]));
  494.     xprintf("word: %s\n", short2str(*word));
  495.     xprintf("last: %s\n", wordno - 2 >= 0 ? short2str(wl[wordno-2]) : "n/a");
  496.     xprintf("this: %s\n", wordno - 1 >= 0 ? short2str(wl[wordno-1]) : "n/a");
  497. #endif /* TDEBUG */
  498.     
  499.     for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) {
  500.     Char  ran[MAXPATHLEN+1],/* The pattern or range X/<range>/XXXX/ */
  501.           com[MAXPATHLEN+1],/* The completion X/XXXXX/<completion>/ */
  502.          *pos = NULL;    /* scratch pointer             */
  503.     int   cmd, sep;        /* the command and separator characters */
  504.  
  505.     if (ptr[0] == '\0')
  506.         continue;
  507.  
  508. #ifdef TDEBUG
  509.     xprintf("match %s\n", short2str(ptr));
  510. #endif /* TDEBUG */
  511.  
  512.     switch (cmd = ptr[0]) {
  513.     case 'N':
  514.         pos = (wordno - 3 < 0) ? nomatch : wl[wordno - 3];
  515.         break;
  516.     case 'n':
  517.         pos = (wordno - 2 < 0) ? nomatch : wl[wordno - 2];
  518.         break;
  519.     case 'c':
  520.     case 'C':
  521.         pos = (wordno - 1 < 0) ? nomatch : wl[wordno - 1];
  522.         break;
  523.     case 'p':
  524.         break;
  525.     default:
  526.         stderror(ERR_COMPILL, "command", cmd);
  527.         return TW_ZERO;
  528.     }
  529.  
  530.     sep = ptr[1];
  531.     if (!Ispunct(sep)) {
  532.         stderror(ERR_COMPILL, "separator", sep);
  533.         return TW_ZERO;
  534.     }
  535.  
  536.     ptr = tw_dollar(&ptr[2], wl, wordno, ran, sep, "pattern");
  537.     ptr = tw_dollar(ptr, wl, wordno, com, sep, "completion"); 
  538.  
  539.     if (*ptr != '\0') {
  540.         if (*ptr == sep)
  541.         *suf = -1;
  542.         else
  543.         *suf = *ptr;
  544.     }
  545.     else
  546.         *suf = '\0';
  547.  
  548. #ifdef TDEBUG
  549.     xprintf("command:    %c\nseparator:  %c\n", cmd, sep);
  550.     xprintf("pattern:    %s\n", short2str(ran));
  551.     xprintf("completion: %s\n", short2str(com));
  552.     xprintf("suffix:     ");
  553.         switch (*suf) {
  554.     case 0:
  555.         xprintf("*auto suffix*\n");
  556.         break;
  557.     case -1:
  558.         xprintf("*no suffix*\n");
  559.         break;
  560.     default:
  561.         xprintf("%c\n", *suf);
  562.         break;
  563.     }
  564. #endif /* TDEBUG */
  565.  
  566.     switch (cmd) {
  567.     case 'p':            /* positional completion */
  568. #ifdef TDEBUG
  569.         xprintf("p: tw_pos(%s, %d) = ", short2str(ran), wordno - 1);
  570.         xprintf("%d\n", tw_pos(ran, wordno - 1));
  571. #endif /* TDEBUG */
  572.         if (!tw_pos(ran, wordno - 1))
  573.         continue;
  574.         return tw_result(com, pat);
  575.  
  576.     case 'N':            /* match with the next-next word */
  577.     case 'n':            /* match with the next word */
  578.     case 'c':            /* match with the current word */
  579.     case 'C':
  580. #ifdef TDEBUG
  581.         xprintf("%c: ", cmd);
  582. #endif /* TDEBUG */
  583.         if ((n = tw_match(pos, ran)) == 0)
  584.         continue;
  585.         if (cmd == 'c')
  586.         *word += n;
  587.         return tw_result(com, pat);
  588.  
  589.     default:
  590.         return TW_ZERO;    /* Cannot happen */
  591.     }
  592.     }
  593.     return TW_ZERO;
  594. } /* end tw_complete */
  595.